package com.lwei.tallybook.service.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lwei.tallybook.base.domain.Account;
import com.lwei.tallybook.base.domain.AccountBook;
import com.lwei.tallybook.base.domain.AccountBookBill;
import com.lwei.tallybook.base.domain.rel.RelBillAccount;
import com.lwei.tallybook.base.domain.rel.RelBookAccount;
import com.lwei.tallybook.base.dto.AccountBillDTO;
import com.lwei.tallybook.base.eums.ConstantEnum;
import com.lwei.tallybook.service.mapper.AccountBookBillMapper;
import com.lwei.tallybook.service.service.*;
import com.lwei.tallybook.service.vo.response.BillResponseVO;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 账单服务类
 * @author sks.lwei
 */
@Log4j2
@Service
public class AccountBookBillServiceImpl extends ServiceImpl<AccountBookBillMapper, AccountBookBill> implements AccountBookBillService {

    @Resource
    private AccountService accountService;

    /**
     * 账本
     */
    @Resource
    private AccountBookService accountBookService;

    /**
     * 账本与用户关联
     */
    @Resource
    private RelBookAccountService relBookAccountService;

    /**
     * 账单与用户关联
     */
    @Resource
    private RelBillAccountService relBillAccountService;

    /**
     * 账单与账本关联
     */
    @Resource
    private RelBillBookService relBillBookService;

    /**
     * 保存 账单 及关联表
     * @param billType 账单类型
     * @param accountBill 账单明细
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveBill(String billType, AccountBillDTO accountBill){
        AccountBookBill accountBookBill = new AccountBookBill(billType, accountBill.getTotal());
        super.save(accountBookBill);
        log.info("账单保存完成");

        List<AccountBillDTO.Bill> billList = accountBill.getBills();
        if(billList!= null && billList.size() > 0){
            List<RelBillAccount> relBillAccountList = new ArrayList<>();
            for (AccountBillDTO.Bill bill : billList) {
                RelBillAccount relBillAccount = new RelBillAccount(bill.getAccountUuid(), bill.getMoney(), accountBookBill.getUuid());
                relBillAccount.setUuid("rba_"+IdWorker.getIdStr());
                relBillAccountList.add(relBillAccount);
            }
            relBillAccountService.saveBatch(relBillAccountList);
            log.info("账单与账户关联表保存完成");
        }

        List<AccountBook> accountBookList = accountBill.getBookList();
        //保存账单与账本关联
        List<String> bookUuids = accountBookList.stream().map(AccountBook::getUuid).collect(Collectors.toList());
        relBillBookService.save(accountBookBill.getUuid(), bookUuids);
        log.info("账单与账本关联保存");
    }

    @Override
    public IPage<AccountBookBill> pageBillByType(IPage<AccountBookBill> page, Wrapper<AccountBookBill> wrapper) {
        return this.page(page, wrapper);
    }

    /* ----------------------------------账单生成------------------------------------------ */

    /**
     * 核心方法
     * 计算账单
     * @param bookList 对应的账本记录.
     * @param endTime 这部分记录获取截止时间
     */
    private AccountBillDTO calculateBill(List<AccountBook> bookList, Date endTime){
        Map<String, Account> accountsCache = accountService.listAll().stream().collect(Collectors.toMap(Account::getUuid, account -> account));

        //获取对应的消费记录uuid
        List<String> bookUuids = bookList.stream().map(AccountBook::getUuid).collect(Collectors.toList());
        //获取对应的账本
        QueryWrapper<AccountBook> accountBookQuery = new QueryWrapper<>();
        accountBookQuery.in("uuid", bookUuids);
        //只查询未结算的
        accountBookQuery.eq("close", false);
        //对应消费记录
        List<AccountBook> accountBookList = accountBookService.list(accountBookQuery);

        double total = 0.0;

        //将消费记录按账本分组  按book-uuid分组
        List<RelBookAccount> relBookAccounts = relBookAccountService.listRelInBookUuid(bookUuids);
        Map<String, List<RelBookAccount>> relMap = relBookAccounts.stream().collect(Collectors.groupingBy(RelBookAccount::getBookUuid));

        log.info("开始生成账单");
        AccountBillDTO accountBill = new AccountBillDTO();
        //每一笔账单
        for (AccountBook accountBook : accountBookList) {
            Double price = accountBook.getConsumePrice();
            total += price;
            //得到该比账单对应的消费记录
            List<RelBookAccount> relBookAccountList = relMap.get(accountBook.getUuid());
            if(relBookAccountList == null || relBookAccountList.size() == 0 ){
                log.warn("账单[{}]的消费记录为空", accountBook.getUuid());
                continue;
            }
            //将记录去重 之后得到总的参与人数
            long count = relBookAccountList.stream().distinct().count();
            //总价除以参与人数 得到每人金额
            double per = price / count;
            for (RelBookAccount relBookAccount : relBookAccountList) {
                String accUuid = relBookAccount.getAccountUuid();
                accountBill.calculateAdd(accountsCache.get(accUuid), per);
            }

            //债主再减去消费
            accountBill.calculateAdd(accountsCache.get(accountBook.getConsumeOwner()), - price);

        }
        accountBill.setDate(endTime == null ? new Date() : endTime);
        accountBill.setBookList(accountBookList);
        accountBill.setTotal(total);
        accountBill.setClose(false);
        accountBill.creatBills();
        return accountBill;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void creatAccountBookBillAll(Date beginTime, Date endTime) {
        creatAccountBookBill(ConstantEnum.BILL_TYPE_WEEK.getKey(), new DateTime(beginTime), new DateTime(endTime));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void creatAccountBookBill(Date endTime) {
        DateTime endTimeOfDay = DateUtil.date(endTime);
        creatAccountBookBill(ConstantEnum.BILL_TYPE_AUTO.getKey(), null, endTimeOfDay);
    }

    /**
     * 生成账单方法
     * @param billType 账单类型
     * @param beginTime 开始时间
     * @param endTime 结束时间
     */
    private void creatAccountBookBill(String billType, DateTime beginTime, DateTime endTime){

        List<AccountBook> accountBookList;
        if(beginTime == null){
            accountBookList = accountBookService.listBookBeforeTime(endTime);
        }else {
            accountBookList = accountBookService.listBookBetweenTime(beginTime, endTime);
        }

        if(accountBookList == null || accountBookList.size() == 0){
            //无记录
            return;
        }
        //计算得到账单明细
        AccountBillDTO accountBill = calculateBill(accountBookList, endTime);
        //保存账单
        saveBill(billType, accountBill);

    }

    /* -------------------------------账单展示---------------------------------------- */

    @Override
    public BillResponseVO showAccountBookBillDaily() {
        Date nowDate = new Date();
        DateTime beginTime = DateUtil.beginOfDay(nowDate);
        List<AccountBook> accountBookList = accountBookService.listBookBetweenTime(beginTime, nowDate);
        if(accountBookList == null || accountBookList.size() == 0){
            return BillResponseVO.getInstance();
        }
        //临时生成的展示账单，所以没有账单uuid
        AccountBillDTO billDTO = calculateBill(accountBookList, nowDate);
        return BillResponseVO.getInstance(billDTO);
    }

    /* ----------------------------------账单获取--------------------------------------- */

    @Override
    public BillResponseVO getAccountBookBillAll(Date beginTime, Date endTime) {
        return getAccountBookBill(ConstantEnum.BILL_TYPE_WEEK.getKey(), beginTime, endTime);
    }

    @Override
    public BillResponseVO getAccountBookBill(String uuid) {
        QueryWrapper<AccountBookBill> wrapper = new QueryWrapper<>();
        wrapper.eq("uuid", uuid);
        AccountBookBill accountBookBill = this.getOne(wrapper);
        return convert(accountBookBill, null);
    }

    private BillResponseVO getAccountBookBill(String billType, Date beginTime, Date endTime){
        QueryWrapper<AccountBookBill> wrapper = new QueryWrapper<>();

        wrapper.eq("bill_type", billType);
        if(beginTime != null && endTime != null){
            //有时间就按时间查询，没时间就全部查询
            wrapper.between("create_time", beginTime, endTime);
        }
        //按照生成日期排序 获取最新的
        wrapper.orderByDesc("create_time");
        wrapper.last("limit 1");
        AccountBookBill accountBookBill = super.getOne(wrapper);
        return convert(accountBookBill, endTime);
    }

    /* ------------------------------账单转换-------------------------------- */


    /**
     * 将bill装换为DTO
     * @param accountBookBill bill
     * @return dto
     */
    private BillResponseVO convert(AccountBookBill accountBookBill, Date endTime){
        Map<String, Account> accountsCache = accountService.listAll().stream().collect(Collectors.toMap(Account::getUuid, account -> account));
        if(accountBookBill == null){
            return BillResponseVO.getInstance();
        }

        //将账单转换为展示形式
        List<RelBillAccount> relBillAccounts = relBillAccountService.listRelBillAccountByAccount(accountBookBill.getUuid());
        AccountBillDTO accountBill = new AccountBillDTO(accountBookBill.getBillPrice(), relBillAccounts, accountsCache);

        accountBill.setBillUuid(accountBookBill.getUuid());
        accountBill.setDate(endTime == null ? new Date() : endTime);
        accountBill.setClose(accountBookBill.getClose());
        accountBill.setCreatDate(accountBookBill.getCreateTime());

        return BillResponseVO.getInstance(accountBill);
    }


    /* -----------------------------其他--------------------------------- */

    @Override
    public AccountBookBill getBillByAccount(String accountUuid) {

        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void killBill(String uuid) {
        UpdateWrapper<AccountBookBill> wrapper = new UpdateWrapper<>();
        wrapper.set("close", true);
        wrapper.eq("uuid", uuid);
        super.update(wrapper);

        //清算掉与之关联的账本
        List<String> bookUuids = relBillBookService.getBookByBill(uuid);
        accountBookService.killAccountBook(bookUuids);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeBill(String uuid) {
        QueryWrapper<AccountBookBill> wrapper = new QueryWrapper<>();
        wrapper.eq("uuid", uuid);
        super.remove(wrapper);
    }
}
